//
// Created by v on 19-6-6.
//

#include "CMVideoDecoder.h"

CMVideoDecoder::CMVideoDecoder() {
    m_buf = NULL;
    m_buflen = 0;
    m_codecOpen = false;
    av_register_all();
}

CMVideoDecoder::~CMVideoDecoder() {
    close();
    if (m_frame) {
        av_frame_free(&m_frame);
    }
    if (m_formatContext) {
        avformat_free_context(m_formatContext);
    }
    m_formatContext = NULL;
}

int CMVideoDecoder::init() {
    m_formatContext = avformat_alloc_context();
    int ret = avformat_open_input(&m_formatContext, m_filename.c_str(), NULL, NULL);
    if (ret != 0) {
        return -1;
    }
    return initCodec();
}

int CMVideoDecoder::initCodec() {
    int i = 0;
    int videoStream = -1;
    int ret = avformat_find_stream_info(m_formatContext, NULL) ;
    if (ret < 0) {
        return -1;
    }
    for (i = 0; i < m_formatContext->nb_streams; i++) {
        if (m_formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStream = i;
        }
    }

    if (videoStream == -1) {
        return -2;
    }
    m_codecContext = m_formatContext->streams[videoStream]->codec;
    m_codecContext->time_base.num = 1;
    m_codecContext->time_base.den = 25;

    m_codec = avcodec_find_decoder(m_codecContext->codec_id);
    if (m_codec == NULL) {
        return -3;
    }

    m_frame = av_frame_alloc();
    return 0;
}

int CMVideoDecoder::open() {
    int ret = avcodec_open2(m_codecContext, m_codec, NULL);
    if (ret < 0) {
        return -1;
    }
    m_codecOpen = true;
    return 0;
}

int CMVideoDecoder::close() {
    if (m_codecOpen) {
        avcodec_close(m_codecContext);
    }
    return 0;
}

int CMVideoDecoder::decoder(char **output, int *outputlen, int *width, int *height) {
    AVPacket packet;
    av_init_packet(&packet);
    int ret = avcodec_send_packet(m_codecContext, &packet);
    if (ret < 0) {
        // TODO: check error
        return -1;
    }

    ret = avcodec_receive_frame(m_codecContext, m_frame);
    if (ret < 0) {
        return -2;
    }

    if (m_buflen < m_frame->pkt_size) {
        m_buf = (char*)realloc(m_buf, m_frame->pkt_size);
        m_buflen = m_frame->pkt_size;
    }
    memcpy(m_buf, m_frame->data[0], m_frame->pkt_size);
    m_datalen = m_frame->pkt_size;

    if (output) {
        *output = m_buf;
    }
    if (outputlen) {
        *outputlen = m_datalen;
    }
    if (width && height) {
        *width = m_frame->width;
        *height = m_frame->height;
    }
    return 0;
}
